package com.dlc.apk.utils

import android.os.Environment
import android.util.Log
import androidx.annotation.IntDef
import java.io.*
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.Executor
import java.util.concurrent.Executors

object LogPlus {
    private var sCurrentLogLevel = Log.DEBUG
    private var sPrefix: String? = null

    /**
     * 初始化LogPlus，可选
     *
     * @param prefix   Tag前缀，可以防止 RIL，IMS，AT，GSM，STK，CDMA，SMS 开头的log不会被打印
     * @param logLevel 打log等级
     */
    fun init(prefix: String?, @LogLevel logLevel: Int) {
        var prefix = prefix
        if (prefix != null) {
            prefix = prefix.trim { it <= ' ' }
            if (prefix.length > 0) {
                sPrefix = prefix
            }
        }
        sCurrentLogLevel = logLevel
    }

    fun v(tag: String?, msg: String, tr: Throwable?) {
        log(Log.VERBOSE, tag, msg, tr)
    }

    fun v(tag: String?, msg: String) {
        log(Log.VERBOSE, tag, msg, null)
    }

    fun v(msg: String, tr: Throwable?) {
        log(Log.VERBOSE, null, msg, tr)
    }

    fun v(msg: String) {
        log(Log.VERBOSE, null, msg, null)
    }

    fun d(tag: String?, msg: String, tr: Throwable?) {
        log(Log.DEBUG, tag, msg, tr)
    }

    fun d(tag: String?, msg: String) {
        log(Log.DEBUG, tag, msg, null)
    }

    fun d(msg: String, tr: Throwable?) {
        log(Log.DEBUG, null, msg, tr)
    }

    fun d(msg: String) {
        log(Log.DEBUG, null, msg, null)
    }

    fun i(tag: String?, msg: String, tr: Throwable?) {
        log(Log.INFO, tag, msg, tr)
    }

    fun i(tag: String?, msg: String) {
        log(Log.INFO, tag, msg, null)
    }

    fun i(msg: String, tr: Throwable?) {
        log(Log.INFO, null, msg, tr)
    }

    fun i(msg: String) {
        log(Log.INFO, null, msg, null)
    }

    fun w(tag: String?, msg: String, tr: Throwable?) {
        log(Log.WARN, tag, msg, tr)
    }

    fun w(tag: String?, msg: String) {
        log(Log.WARN, tag, msg, null)
    }

    fun w(msg: String, tr: Throwable?) {
        log(Log.WARN, null, msg, tr)
    }

    fun w(msg: String) {
        log(Log.WARN, null, msg, null)
    }

    fun e(tag: String?, msg: String, tr: Throwable?) {
        log(Log.ERROR, tag, msg, tr)
    }

    fun e(tag: String?, msg: String) {
        log(Log.ERROR, tag, msg, null)
    }

    fun e(msg: String, tr: Throwable?) {
        log(Log.ERROR, null, msg, tr)
    }

    fun e(msg: String) {
        log(Log.ERROR, null, msg, null)
    }

    private fun log(logLevel: Int, tag: String?, msg: String, tr: Throwable?) {
        var tag = tag
        var msg: String? = msg
        if (logLevel < sCurrentLogLevel) {
            return
        }
        val e = Thread.currentThread().stackTrace[4]
        val fileName = e.fileName
        val lineNum = e.lineNumber
        val methodName = e.methodName
        val sb = StringBuilder()
        sb.append(methodName)
            .append('(')
            .append(fileName)
            .append(':')
            .append(lineNum)
            .append(')')
            .append(msg)
        msg = sb.toString()
        sb.delete(0, sb.length)

        // 注意，如果tag是"IMS"开头的(还有其他)，log会不打印，所以加个"前缀_"
        // 参考这里 http://stackoverflow.com/a/36469141/5324526
        if (sPrefix != null) {
            sb.append(sPrefix).append('_')
        }
        if (tag == null || tag.length == 0 || tag.trim { it <= ' ' }.length == 0) {
            val className = e.className
            val index = className.lastIndexOf('.') + 1
            sb.append(className, index, className.length)
        } else {
            sb.append(tag)
        }
        tag = sb.toString()
        if (tr == null) {
            when (logLevel) {
                Log.VERBOSE -> Log.v(tag, msg)
                Log.DEBUG -> Log.d(tag, msg)
                Log.INFO -> Log.i(tag, msg)
                Log.WARN -> Log.w(tag, msg)
                Log.ERROR -> Log.e(tag, msg)
                else -> {
                }
            }
        } else {
            when (logLevel) {
                Log.VERBOSE -> Log.v(tag, msg, tr)
                Log.DEBUG -> Log.d(tag, msg, tr)
                Log.INFO -> Log.i(tag, msg, tr)
                Log.WARN -> Log.w(tag, msg, tr)
                Log.ERROR -> Log.e(tag, msg, tr)
                else -> {
                }
            }
        }
        if (isSaveToFile) {
            /*todo 保存日志到本地*/
            val finalTag: String = tag
            val finalMsg: String = msg
            threadPoolExecutor.execute {
                deleteYesterdayLogFile()
                saveLogToFile(finalTag, finalMsg, tr)
            }
            if (logListener != null) {
                logListener!!.onLog(finalTag, msg, tr)
            }
        }
    }

    private val threadPoolExecutor: Executor = Executors.newFixedThreadPool(3)
    private fun saveLogToFile(tag: String, msg: String, th: Throwable?) {
        val logFile = todayLogFile ?: return
        //e("###日志文件路径:" + logFile.getAbsolutePath());
        var sdf = threadLocal.get()
        if (sdf == null) {
            threadLocal.set(MultiSimpleDateFormat(SimpleDateFormat("yyyyMMdd"), SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")))
        }
        sdf = threadLocal.get()
        if (sdf == null) {
            return
        }
        var fileOutputStream: FileOutputStream? = null
        try {
            fileOutputStream = FileOutputStream(logFile, true)
            val log = String.format("[ %s ] %s ========> %s\n", sdf.yyyyMMddHHmmssSSS.format(Date()), tag, msg)
            fileOutputStream.write(log.toByteArray())
            if (th != null) {
                fileOutputStream.write(getStackTraceInfo(th).toByteArray())
            }
            fileOutputStream.close()
        } catch (e: Exception) {
            e.printStackTrace()
        } finally {
            if (fileOutputStream != null) {
                try {
                    fileOutputStream.close()
                } catch (e: IOException) {
                    e.printStackTrace()
                }
            }
        }
    }

    private fun deleteYesterdayLogFile() {
        try {
            val yesterdayLogFile = yesterdayLogFile
            if (yesterdayLogFile != null && yesterdayLogFile.exists()) {
                yesterdayLogFile.delete()
            }
        } catch (ex: Exception) {
            ex.printStackTrace()
        }
    }

    private var isSaveToFile = false
    private var logFileName: String? = null
    fun setSaveLogToFile(isSaveToFile: Boolean, logFileName: String?) {
        LogPlus.isSaveToFile = isSaveToFile
        LogPlus.logFileName = logFileName
    }

    val todayLogFile: File?
        get() {
            var sdf = threadLocal.get()
            if (sdf == null) {
                threadLocal.set(MultiSimpleDateFormat(SimpleDateFormat("yyyyMMdd"), SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")))
            }
            sdf = threadLocal.get()
            return if (sdf == null) {
                null
            } else File(String.format("%s/%s_%s.log", Environment.getExternalStorageDirectory().toString(), logFileName, sdf.yyyyMMdd.format(Date())))
        }

    fun getLogFile(date: String?): File {
        return File(String.format("%s/%s_%s.log", Environment.getExternalStorageDirectory().toString(), logFileName, date))
    }

    val yesterdayLogFile: File?
        get() {
            var sdf = threadLocal.get()
            if (sdf == null) {
                threadLocal.set(MultiSimpleDateFormat(SimpleDateFormat("yyyyMMdd"), SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")))
            }
            sdf = threadLocal.get()
            return if (sdf == null) {
                null
            } else File(String.format("%s/%s_%s.log", Environment.getExternalStorageDirectory().toString(), logFileName, sdf.yyyyMMdd.format(Date(System.currentTimeMillis() - 1000 * 60 * 60 * 24))))
        }
    private val threadLocal = ThreadLocal<MultiSimpleDateFormat>()
    fun getStackTraceInfo(throwable: Throwable?): String {
        if (throwable == null) {
            return ""
        }
        val pw: PrintWriter
        val writer = StringWriter()
        try {
            pw = PrintWriter(writer)
            throwable.printStackTrace(pw)
        } catch (ex: Exception) {
        }
        return writer.toString()
    }

    var logListener: ILogListener? = null

    @IntDef(Log.VERBOSE, Log.DEBUG, Log.INFO, Log.WARN, Log.ERROR)
    @Retention(RetentionPolicy.SOURCE)
    annotation class LogLevel
    private class MultiSimpleDateFormat(var yyyyMMdd: SimpleDateFormat, var yyyyMMddHHmmssSSS: SimpleDateFormat)
    interface ILogListener {
        fun onLog(tag: String?, log: String?, th: Throwable?)
    }
}